package middleware

import (
	"net/http"
	"strings"
	"time"

	"github.com/go-redis/redis/v7"
	http2 "go-api-template/api/http"
	e2 "go-api-template/internal/e"
	"go-api-template/internal/util/listUtil"
	tokenUtil2 "go-api-template/internal/util/tokenUtil"
	"go-api-template/service"

	"github.com/gin-gonic/gin"
)

var (
	// LoginWhiteList 登录白名单
	LoginWhiteList = []string{
		"/api/base/login",
		"/api/base/getCaptcha",
	}
)

// UserLoginMid 用户登录中间件
func UserLoginMid() gin.HandlerFunc {
	return func(c *gin.Context) {
		if listUtil.IsInStringList(LoginWhiteList, c.FullPath()) {
			c.Next()
			return
		}
		token := c.GetHeader("token")
		if token == "" {
			// 兼容WS，从url获取
			token = c.Query("token")
			if token == "" {
				c.String(http.StatusUnauthorized, "Please Login")
				c.Abort()
				return
			}
		}
		uc, err := tokenUtil2.TokenParseClaims(token)
		if err != nil {
			c.JSON(401, gin.H{
				"code": e2.LoginIllegal.Code,
				"msg":  err.Error(),
			})
			c.Abort()
			return
		}

		// 后台接口校验权限
		if IsAdminPath(c.FullPath()) && !uc.IsAdmin {
			c.JSON(401, gin.H{
				"code": e2.NotAllow.Code,
				"msg":  e2.NotAllow.Msg,
			})
			c.Abort()
			return
		}

		c.Set("user_id", uc.Id)
		c.Next()
	}
}

// UserOneLoginMid 单态登录中间件
func UserOneLoginMid() gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp http2.Resp
		userId := c.GetString("user_id")
		token, _ := c.Cookie("token")
		cacheKey := e2.UserTokenCache + userId

		// 登录接口，踢出其他端
		if find := strings.Contains(c.FullPath(), "login"); find {
			service.Cache.Del(cacheKey)
		} else {
			// 判断缓存中是否存在token - key
			t := service.Redis.Get(cacheKey).Val()
			if t != "" && token != t {
				resp.SetCode(e2.LoginKickOut)
				c.JSON(200, resp)
				c.Abort()
			}
		}

		c.Next()
	}
}

// UserLoginLimit 登录请求限制（防止字典爆破等）
func UserLoginLimit() gin.HandlerFunc {
	return func(c *gin.Context) {
		var resp http2.Resp
		redisKey := e2.UserLoginLimitCache + c.ClientIP()

		if query := strings.Contains(c.FullPath(), "user/login"); query {
			// 基于客户端IP进行登录限制
			loginCount, err := service.Redis.Get(redisKey).Int()
			if err != nil {
				if err == redis.Nil {
					service.Redis.Set(redisKey, 0, 60*time.Second)
				}
				service.Log.Error(err.Error())
			} else {
				if loginCount >= service.Conf.Security.Login.Retry && loginCount <= service.Conf.Security.Login.MaxRetry {
					err := service.Cache.Set(redisKey, loginCount+1, 60*time.Second)
					if err != nil {
						log.Error(err.Error())
						return
					}
					resp.SetCode(e2.LoginTooMany)
					c.JSON(200, resp)
					c.Abort()
				} else if loginCount > 100 {
					// TODO: 将IP加入黑名单
					resp.SetCode(e2.LoginIllegal)
					c.JSON(200, resp)
				} else {
					// 缓存登录错误次数,一小时后过期
					err := service.Cache.Set(redisKey, loginCount+1, 60*time.Second)
					if err != nil {
						log.Error(err.Error())
						return
					}
				}
			}

			// 过滤黑名单
		}
		c.Next()
	}
}
